package br.com.pentagrama.service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import br.com.pentagrama.exception.PingException;
import br.com.pentagrama.util.ParseUtil;

/**
 * http://razec.wordpress.com/2011/02/04/bug-solucao-inetaddress-isreachable3000-2/
 * @author Pablo
 *
 */

public class PingICMP {
	
	private long timeout = 250; //milisegundos
	
	public PingICMP() {
	}
	
	public PingICMP(long timeout) {
		this.timeout = timeout;
	}
	

	/**
	 *  This is basic method for pinging host. It checks your system's name and
	 *  timeout value and calls aprioprate method, wich executes ping command
	 *
	 *@param  target                    host name or IP (v4) to ping
	 *@param  count                     number of pingecho packets to send
	 *@return                           true if target host is reachable; false
	 *@exception  IOException           if an I/O error occurs
	 *@exception  InterruptedException  if the current thread is interrupted by
	 *      another thread while it is waiting, then the wait is ended and an
	 *      InterruptedException is thrown.
	 * @throws PingException 
	 */
	public long ping(String target) throws IOException, InterruptedException, PingException {
		delay(1000);
		String os = System.getProperty("os.name");
		if (os.startsWith("Windows")) {
			return pingWin(target, timeout);
		}
		else {
			// o ping unix não possui timeout
			return pingUnix(target);
			
			/*if (timeout != 0) {
				return pingUnix(target, count, timeout);
			}
			else {
				return pingUnix(target, count);
			}*/
		}
	}


	/**
	 * Ping em sistema operacional windows.
	 * 
	 * @param target destino do ping
	 * @param timeout tempo limite de espera pelo ping
	 * 
	 * @return o tempo em milisegundos do ping
	 * 
	 * @throws IOException
	 * @throws InterruptedException
	 * @throws PingException
	 */
	private long pingWin(String target, long timeout)
		throws IOException, InterruptedException, PingException {
		String[] cmd = {};
		if (timeout == 0) {
			cmd = new String[6];
		}
		else {
			cmd = new String[8];
			cmd[5] = "-w";
			cmd[6] = Long.toString(timeout);
		}
		cmd[0] = "ping";
		cmd[1] = "-n";
		cmd[2] = Integer.toString(1); //sempre executa apenas 1 ping
		cmd[3] = "-l"; 
		cmd[4] = "750";
		cmd[cmd.length - 1] = target;
		return ping(cmd);
	}


	//miliseconds in second
	private final static long SECMSEC = 1000;

	//delay between checks
	private final static long DELAY = 500;

	/**
	 * O método não implementa o retorno correto do tempo do ping
	 */
	@Deprecated
	public long pingUnix(String target, int count, long timeout)
		throws IOException, InterruptedException {
		Process ping = Runtime.getRuntime().exec(new String[]{"ping", "-c", Integer.toString(count), target});
		//sleep until we can expect result, ie. DELAY or temeout after last ping, whichever comes sooner
		long sleep = count * SECMSEC - 1 + Math.min(timeout, DELAY);
		timeout -= Math.min(timeout, DELAY);
		Thread.sleep(sleep);
		//if initial timeout was less than DELAY than timeout is already 0, but still we must check status
		do {
			//this makes timeout is always nonnegative
			timeout -= Math.min(timeout, DELAY);
			Thread.sleep(timeout);
			try {
				//return ping.exitValue() == 0;
			}
			catch (IllegalThreadStateException e) {
				/*
				 *  process still running; do nothing; wait another DELAYms
				 */
			}
		} while (timeout > 0);
		//timeout reached
		return 0;
	}

	/**
	 * Ping em sistema Unix
	 * 
	 * @param target destino do ping
	 * 
	 * @return tempo em milisegundos de retorno do ping
	 * 
	 * @throws IOException
	 * @throws InterruptedException
	 * @throws PingException
	 */
	private long pingUnix(String target)
		throws IOException, InterruptedException, PingException {
		return ping(new String[]{"ping", "-c", Integer.toString(1), target});
	}

	/**
	 * Ping usando comando do Sistema Operacinal.
	 * 
	 * @param cmd parâmetros do comando
	 * @return tempo em milisegundos para resposta do ping
	 * 
	 * @throws IOException
	 * @throws InterruptedException
	 * @throws PingException
	 */
	private long ping(String[] cmd)
		throws IOException, InterruptedException, PingException {
		Process proccess = Runtime.getRuntime().exec(cmd);
		int waitFor = proccess.waitFor();
		
		if(waitFor != 0) {
			throw new PingException("Host não respondeu ao ping.");
		}
		
		String linha = "";
		StringBuilder stringBuilder = new StringBuilder();
        BufferedReader buf = new BufferedReader(new InputStreamReader(proccess.getInputStream()));
        while((linha = buf.readLine()) != null) {
            stringBuilder.append(linha);
        }
	        
		return ParseUtil.getMiliSegundos(stringBuilder.toString());
	}
	
	public void delay(int tempo) {
		try {
			Thread.sleep(tempo);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
